package com.lily.aidouzixun.threadpool;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 *
 * A wrapper of ThreadPoolExecutor in JDK
 * The hook of afterExecute was overwritten.
 *
 */
public class PoolExecutors extends ThreadPoolExecutor {

    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if (canAddWorkFromLifeGuard()) {
            addWorkFromLifeGuard();
        }
    }


    public PoolExecutors(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                         TimeUnit unitTime,
                         BlockingQueue<Runnable> workQueue,
                         ThreadFactory threadFactory,
                         RejectedExecutionHandler rejectedExecutionHandler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime,
                unitTime, workQueue, threadFactory, rejectedExecutionHandler);
    }

    public PoolExecutors(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                         TimeUnit unitTime, BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime,
                unitTime, workQueue,
                Executors.defaultThreadFactory(),
                new RejectedTaskHandler());
    }

    public PoolExecutors(int corePoolsize, int maximumPoolSize, long keepAliveTime,
                         BlockingQueue<Runnable> workQueue) {
        this(corePoolsize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue,
                Executors.defaultThreadFactory(),
                new RejectedTaskHandler());
    }

    public PoolExecutors(int corePoolsize, int maximumPoolSize, BlockingQueue<Runnable> workQueue) {
        this(corePoolsize, maximumPoolSize, 60L, TimeUnit.SECONDS, workQueue,
                Executors.defaultThreadFactory(),
                new RejectedTaskHandler());
    }

    private boolean blocked;

    public boolean isBlocked() {
        if (getQueue().remainingCapacity() > 0) {
            blocked = false;
        } else {
            blocked = true;
        }
        return blocked;
    }

    public boolean canAddWorkFromLifeGuard() {
        RejectedTaskHandler tmpHamdler = (RejectedTaskHandler) getRejectedExecutionHandler();
        if (!isBlocked() && !tmpHamdler.isLifeGuardEmpty()) {
            return true;
        } else {
            return false;
        }
    }

    private ReentrantLock taskRegainLock = new ReentrantLock();

    public void addWorkFromLifeGuard() {
        RejectedTaskHandler tmpHamdler = (RejectedTaskHandler) getRejectedExecutionHandler();
        try {
            taskRegainLock.lock();
            while ((getQueue().remainingCapacity() > 0) &&
                    !((RejectedTaskHandler) getRejectedExecutionHandler()).isLifeGuardEmpty()) {
                getQueue().put(tmpHamdler.getTaskFromLifeGuard());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            taskRegainLock.unlock();
        }
    }


    /**
     * Factory method for SubThreadPool
     */
    public static PoolExecutors newLimitRejectedThreadPoolWithCapcity(int corePoolsize, int maximunPoolsize,
                                                                      int workQueueCapacity) {
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(workQueueCapacity);
        return new PoolExecutors(corePoolsize, maximunPoolsize, workQueue);
    }

    public static PoolExecutors newLimitRejectedThreadPool(int corePoolsize, int maximumPoolsize) {
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(30);
        return new PoolExecutors(corePoolsize, maximumPoolsize, workQueue);
    }

    public static PoolExecutors newLimitRejectedThreadPool(int nthread) {
        return new PoolExecutors(nthread, 50, new LinkedBlockingQueue<Runnable>(30));
    }
}

